#ifndef QQ_NETWORK
#define QQ_NETWORK

#include <qapplication.h>
#include <qcryptographichash.h>
#include <qjsondocument.h>
#include <qjsonobject.h>
#include <qmetaobject.h>
#include <qobject.h>
#include <qtcpsocket.h>
#include <qthread.h>
#include <qqueue.h>
#include <qtimer.h>

#include <boost/lockfree/spsc_queue.hpp>

#include "core/QQSettings.h"

#define qq_network QQNetWork::getInstance()

/*
class QQNetWorkReadThread : public QThread
{
public:
	QQNetWorkReadThread(QObject* parent = nullptr);
	~QQNetWorkReadThread();

	void setTcpSocket(QTcpSocket* tcpSocket);

	void subscribeNetwork(const QObject* receiver, const QString& key, const QString& slot);
	void unsubscribeNetwork(const QString& key);
protected:
	void run() override;
private:
	void notify(const QByteArray& data);
private:
	QByteArray buffer;
	QTcpSocket* mTcpSocket;
	QMutex mMutex;
	QMap<QString, QObject*> mReceivers;
	QMap<QString, QString> mSlots;
};


class QQNetWorkWriteThread : public QThread
{
public:
	QQNetWorkWriteThread(QObject* parent = nullptr);
	~QQNetWorkWriteThread();
protected:
	void run() override;
private:
	void submit(const QByteArray& work);
private:
	QAtomicInteger<int> mTcpConnectState = QTcpSocket::UnconnectedState;
	QTcpSocket* mTcpSocket;
	boost::lockfree::spsc_queue<QByteArray>* mWrites;
};
*/

class QQNetWork : public QObject
{
	Q_OBJECT
private:
	struct QQNetWorkPackage
	{
		QString key;
		QByteArray data;
		int retryCount;
	};
private:
	QQNetWork(QObject* parent = nullptr);
	~QQNetWork();
public:
	static QQNetWork* getInstance();
	void init();
	void subscribeNetwork(const QObject* receiver, const QString& key, const QString& slot);
	void unsubscribeNetwork(const QString& key);
	//entry
	void entry(const QString& account, const QString& password);
	void registerAccount(const QString& nickname, const QString& phone, const QString& password);
	void findPassword(const QString& account, const QString& phone);
	//chat
	void sendMessage(const QString& sender, const QString& receiver, const QString& message);
	void sendFile(const QString& sender, const QString& receiver, const QString& fileName);
	void sendFile(const QString& sender, const QString& receiver, const QString& key, int index, int totalPieceCount, const QByteArray& data);
	void sendAudioRequest(const QString& sender, const QString& receiver);
	void cancelAudioRequest(const QString& sender, const QString& receiver);
	void sendAudioResponse(const QString& sender, const QString& receiver, bool accept);
	void sendVideoRequest(const QString& sender, const QString& receiver);
	void cancelVideoRequest(const QString& sender, const QString& receiver);
	void sendVideoResponse(const QString& sender, const QString& receiver, bool accept);
	//friend
	void addFriend(const QString& account, const QString& friendAccount);
	void deleteFriend(const QString& account, const QString& friendAccount);
	void queryFriend(const QString& account);
	void agreeFriendRequest(const QString& account, const QString& friendAccount);
	void rejectFriendRequest(const QString& account, const QString& friendAccount);
signals:
	void connectStateChanged(bool isConnected);
	void errorOccurred(QAbstractSocket::SocketError error);
private slots:
	void doReadyRead();
	void doHandleData(const QJsonObject& data);
	void doTcpConnected();
	void doTcpDisconnected();
	void doTimeOut();
	void doTcpError(QAbstractSocket::SocketError error);
private:
	void package(const QByteArray& data);
	void send(const QQNetWorkPackage& netWorkPackage);
	void notify(const QByteArray& data);
	void updateToken(const QString& token);
	void updateQueue(const QString& key);
private:
	QMap<QString, QObject*> mReceivers;
	QMap<QString, QString> mSlots;
	QMap<QString, QQNetWorkPackage> mSendedPackages;
	QQueue<QQNetWorkPackage> mUnsendedPackages;

	QTimer* mTimer;
	QTcpSocket* mTcpSocket;
	QString mToken;
	bool mIsConnected;
	int mMaxRetryCount = 3;
};

#endif // !QQ_NETWORK